package common

import (
	"fmt"
	cache2 "git.mycaigou.com/gfyx/common/cache"
	"git.mycaigou.com/gfyx/common/rpc_client"
	"git.mycaigou.com/gfyx/micro-gfyx-mp-api/http_server/controller"
	"git.mycaigou.com/gfyx/micro-gfyx-mp-api/infrastructure/common/errcode"
	commonDto "git.mycaigou.com/gfyx/micro-gfyx-mp-api/infrastructure/dto/common"
	"git.mycaigou.com/gfyx/micro-gfyx-mp-api/infrastructure/utils"
	"git.mycaigou.com/gfyx/micro-gfyx-mp-api/service/base/user/mp"
	"git.mycaigou.com/gfyx/micro-gfyx-mp-api/service/base/user/user_service"
	commonService "git.mycaigou.com/gfyx/micro-gfyx-mp-api/service/common"
	"git.myscrm.cn/golang/stark/v4"
	"github.com/gin-gonic/gin"
)

type MpController struct {
	controller.BaseController
	mpUserService mp.MpUserServiceIface
	userService   user_service.UserSerivceIface
	mpService     commonService.MiniProgramServiceIface
}

func NewMpController(
	mpUserService mp.MpUserServiceIface,
	userService user_service.UserSerivceIface,
	mpService commonService.MiniProgramServiceIface,
) *MpController {
	return &MpController{
		mpUserService: mpUserService,
		userService:   userService,
		mpService:     mpService,
	}
}

const CHECK_CLEAN_CODE = "check_clean_code"

func (c *MpController) RegisterRouter(g *gin.RouterGroup) {
	// 微信小程序账号绑定
	g.POST("/bind", c.MpUserBind)
	// 微信小程序静默登录
	g.GET("/login", c.MpUserLogin)
	// 开发模拟微信小程序登录
	g.POST("/simulate-login", c.SimulateMpUserLogin)
	// 申请开通账号信息登记
	g.POST("/apply", c.UserApply)
	// 清除 token 缓存
	g.GET("/clean", c.CleanWechatToken)
	// 手机验证码登录
	g.POST("/sms-code-login", c.SmsCodeLogin)
}

type UserApplyParams struct {
	MobileCode string `json:"mobile_code"`
}

func (c *MpController) getMobileCodeKey(mobileCode string) string {
	return fmt.Sprintf("gfyx-mc-%s", mobileCode)
}

// Apply
// 申请开通账号信息登记
func (c *MpController) UserApply(ctx *gin.Context) {
	// 临时记录gin的请求
	stark.Logger.Info(ctx, "URI:", ctx.Request.RequestURI, ".BODY:", ctx.Request.Body)

	req := &UserApplyParams{}
	if err := ctx.ShouldBind(req); err != nil {
		c.Fail(ctx, errcode.COMMON_PARAMS_ERROR, err.Error())
		return
	}

	if req.MobileCode == "" {
		c.Fail(ctx, errcode.COMMON_PARAMS_ERROR, "mobile_code为空")
		return
	}

	cache, _ := cache2.Redis()
	key := c.getMobileCodeKey(req.MobileCode)
	mobile, err := cache.Get(ctx, key)
	if err != nil {
		stark.Logger.Errorf(ctx, "UserApply-cache: %s, mobile: %v, err: %+v", key, mobile, err)
		c.Fail(ctx, errcode.COMMON_PARAMS_ERROR, err.Error())
		return
	}

	if mobile == "" {
		stark.Logger.Infof(ctx, "UserApply-cache: %s, mobile: %v, err: %+v", key, mobile, err)
		c.Success(ctx, map[string]interface{}{"result": "手机号为空"})
		return
	}

	err = c.userService.UserApply(ctx, mobile)
	if err != nil {
		c.Fail(ctx, errcode.COMMON_PARAMS_ERROR, err.Error())
		return
	}

	c.Success(ctx, nil)
}

type UserBindParams struct {
	Code          string `json:"code"`
	OpenId        string `json:"openid"`
	MobileCode    string `json:"mobile_code"`
	EncryptedData string `json:"encrypted_data"`
	Iv            string `json:"iv"`
}

// MpUserBind
// openid与手机号绑定
func (c *MpController) MpUserBind(ctx *gin.Context) {
	// 临时记录gin的请求
	stark.Logger.Info(ctx, "URI:", ctx.Request.RequestURI, ".BODY:", ctx.Request.Body)

	req := &UserBindParams{}
	sessionKey := ""
	if err := ctx.ShouldBind(req); err != nil {
		c.Fail(ctx, errcode.COMMON_PARAMS_ERROR, err.Error())
		return
	}

	// 老版本微信mobileCode可能为空。有mobilecode 取mobile code 来获取手机号，没有时就解密数据
	if req.MobileCode == "" && (req.EncryptedData == "" || req.Iv == "") {
		c.Fail(ctx, errcode.COMMON_PARAMS_ERROR, "mobile_code或encrypted_data/iv为空")
		return
	}

	if req.Code == "" && req.OpenId == "" {
		c.Fail(ctx, errcode.COMMON_PARAMS_ERROR, "code or openid为空")
		return
	}
	unionId := ""
	if req.Code != "" {
		mpLoginInfo, err := c.mpUserService.MpLogin(ctx, req.Code)
		if err != nil {
			stark.Logger.Errorf(ctx, "mplogininfo-err-%+v", mpLoginInfo)
			c.Fail(ctx, errcode.COMMON_PARAMS_ERROR, err.Error())
			return
		}

		if mpLoginInfo == nil || mpLoginInfo.OpenId == "" {
			stark.Logger.Errorf(ctx, "mplogininfo-%+v", mpLoginInfo)
			c.Fail(ctx, errcode.COMMON_DATA_NOT_EXIST, "获取openID失败")
			return
		}

		sessionKey = mpLoginInfo.SessionKey
		req.OpenId = mpLoginInfo.OpenId
		unionId = mpLoginInfo.UnionId
	}

	if req.OpenId == "" {
		c.Fail(ctx, errcode.COMMON_PARAMS_ERROR, "openid为空")
		return
	}

	//sessionKey = "cupcRy3ApQb9+f7erxpBQw=="
	mobile, err := c.mpUserService.GetMpMobile(ctx, req.MobileCode, req.EncryptedData, req.Iv, sessionKey)
	if err != nil {
		stark.Logger.Infof(ctx, "GetMpMobile-err:%+v, openid: %s", err, req.OpenId)
		c.Fail(ctx, errcode.COMMON_PARAMS_ERROR, err.Error())
		return
	}

	// 缓存手机号用于账号绑定失败是登记申请人信息
	mKey := c.getMobileCodeKey(req.MobileCode)
	cache, _ := cache2.Redis()
	cache.Set(ctx, mKey, mobile, true, 24*3600)

	loginInfo, err := c.userService.MpBindUser(ctx, req.OpenId, mobile, unionId)
	if err != nil {
		grpcErr := rpc_client.GetGrpcError(err)
		errCode := errcode.COMMON_USER_BIND_FAIL
		errMessage := err.Error()
		if grpcErr != nil && grpcErr.GetCode() == 20020004 {
			errCode = errcode.COMMON_USER_BINDED
			errMessage = grpcErr.GetMessage()
		}

		c.Fail(ctx, errCode, errMessage)
		return
	}

	c.Success(ctx, loginInfo)
}

// MpUserLogin
// 静默登录
func (c *MpController) MpUserLogin(ctx *gin.Context) {
	// 临时记录gin的请求
	stark.Logger.Info(ctx, "URI:", ctx.Request.RequestURI, ".BODY:", ctx.Request.Body)

	code := ctx.Query("code")
	if code == "" {
		c.Fail(ctx, errcode.COMMON_MP_USER_LACK_CODE, "code为空")
		return
	}

	mpLoginInfo, err := c.mpUserService.MpLogin(ctx, code)
	if err != nil {
		c.Fail(ctx, errcode.COMMON_MP_USER_LOGIN_FAILD, err.Error())
		return
	}

	if mpLoginInfo == nil || mpLoginInfo.OpenId == "" {
		stark.Logger.Errorf(ctx, "mplogininfo-%+v", mpLoginInfo)
		c.Fail(ctx, errcode.COMMON_MP_USER_GET_OPEN_ID_FAILD, "获取openID失败")
		return
	}

	loginInfo, err := c.userService.MpUserLogin(ctx, mpLoginInfo.OpenId, mpLoginInfo.UnionId)
	if err != nil {
		grpcErr := rpc_client.GetGrpcError(err)
		errCode := errcode.COMMON_USER_UNBIND
		errMessage := err.Error()
		if grpcErr != nil && grpcErr.GetCode() == 20020002 {
			errCode = errcode.COMMON_USER_FORBIDDEN
			errMessage = grpcErr.GetMessage()
		}

		c.Fail(ctx, errCode, errMessage)
		return
	}

	if err != nil {
		c.Fail(ctx, errcode.COMMON_USER_UNBIND, err.Error())
		return
	}

	c.Success(ctx, loginInfo)
}

// 静默登录
func (c *MpController) SimulateMpUserLogin(ctx *gin.Context) {
	openId, exist := ctx.GetPostForm("open_id")
	unionId, _ := ctx.GetPostForm("union_id")
	if !exist {
		c.Fail(ctx, errcode.COMMON_DATA_NOT_EXIST, "没有传递openID")
	}

	loginInfo, err := c.userService.MpUserLogin(ctx, openId, unionId)
	if err != nil {
		c.Fail(ctx, errcode.COMMON_USER_UNBIND, err.Error())
		return
	}

	if err != nil {
		c.Fail(ctx, errcode.COMMON_USER_UNBIND, err.Error())
		return
	}

	c.Success(ctx, loginInfo)
}

// 手机验证码登录
func (c *MpController) SmsCodeLogin(ctx *gin.Context) {
	request := &commonDto.SmsLoginRequest{}
	if err := ctx.ShouldBind(request); err != nil {
		stark.Logger.Error(ctx, "错误code:", errcode.COMMON_PARAMS_ERROR, ",错误msg:,", err)
		c.Fail(ctx, errcode.COMMON_PARAMS_ERROR, "参数异常")
		return
	}
	rs, err := c.userService.SmsLogin(ctx, request)
	if err != nil {
		stark.Logger.Info(ctx, "错误code:%s,错误msg:%s", errcode.COMMON_DATA_NOT_EXIST, err.Error())
		c.Fail(ctx, errcode.COMMON_DATA_NOT_EXIST, err.Error())
		return
	}
	c.Success(ctx, rs)
}

func (c *MpController) CleanWechatToken(ctx *gin.Context) {
	code := ctx.Query("code")
	checkCode := stark.Configer.(stark.ApolloConfigerIface).GetStringValue(CHECK_CLEAN_CODE, "")
	if code != checkCode {
		c.Fail(ctx, 403, "密钥不正确")
		return
	}
	err := c.mpUserService.CleanMpAccessToken(ctx)
	if err != nil {
		stark.Logger.Info(ctx, err)
		c.Fail(ctx, 500, "清除缓存失败")
		return
	}
	c.Success(ctx, "")
}

func (c *MpController) RegisterMiniProgramRouter(g *gin.RouterGroup) {
	g.GET("qrcode-src-info", c.qrcodeSrcInfo)
}

func (c *MpController) qrcodeSrcInfo(ctx *gin.Context) {
	var request commonDto.GetQrCodeInfoByIdRequest
	if err := ctx.ShouldBind(&request); err != nil {
		stark.Logger.Errorf(ctx, "http getQrCode 参数错误：%s", err.Error())
		c.Fail(ctx, errcode.COMMON_PARAMS_ERROR, utils.GetValidateErr(request, err))
		return
	}

	resp, err := c.mpService.GetQrCodeInfoById(ctx, &commonDto.GetQrCodeInfoByIdRequest{QrId: request.QrId})
	if err != nil {
		c.Fail(ctx, errcode.COMMON_ERROR, err.Error())
		return
	}

	c.Success(ctx, map[string]string{"page": resp.Page, "scene": resp.Scene})
}
